package com.juzipi.framework.security.handler;

import com.juzipi.common.constant.Constants;
import com.juzipi.common.constant.json.ResultCode;
import com.juzipi.common.constant.properties.PropertiesVariable;
import com.juzipi.common.tools.Result;
import com.juzipi.common.tools.ResultTool;
import com.juzipi.common.utils.json.JsonAuthentication;
import com.juzipi.framework.web.service.LoginUserService;
import es.moki.ratelimitj.core.limiter.request.RequestLimitRule;
import es.moki.ratelimitj.core.limiter.request.RequestRateLimiter;
import es.moki.ratelimitj.inmemory.request.InMemorySlidingWindowRequestRateLimiter;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.*;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.web.authentication.AuthenticationFailureHandler;
import org.springframework.stereotype.Component;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.time.Duration;
import java.util.Collections;
import java.util.Set;

/**
 * 登陆失败
 */
@Component
public class MyAuthenticationFailureHandler extends JsonAuthentication implements AuthenticationFailureHandler {


    @Autowired
    private LoginUserService loginUserService;


    //10分钟 10 次机会,在这个类里给静态变量注入配置文件值始终读取不到，换一个类就行了，真是奇怪
    Set<RequestLimitRule> rules = Collections.singleton(RequestLimitRule.of(Duration.ofMinutes(PropertiesVariable.userPeriodTime),PropertiesVariable.userTimesLock));
    RequestRateLimiter requestLimitRule = new InMemorySlidingWindowRequestRateLimiter(rules);


    @Override
    public void onAuthenticationFailure(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, AuthenticationException e) throws IOException, ServletException {
        //直接通过登录参数获取用户名，不改的话固定就是username了
        String username = httpServletRequest.getParameter(Constants.USERNAME);
        Result result = null;
        //账号过期
        if (e instanceof AccountExpiredException){
            result = ResultTool.failCustomize(ResultCode.USER_ACCOUNT_EXPIRED);
        }
        //密码错误
        if (e instanceof BadCredentialsException){
            //计数器加1，判断用户是否触发了锁定
            boolean reachLimit = requestLimitRule.overLimitWhenIncremented(username);
            if (reachLimit){
                //达到限制就锁定用户
                loginUserService.lockUserByUsername(username);
                result = ResultTool.failCustomize(ResultCode.USER_ACCOUNT_LOCKED);
            }
            result = ResultTool.failCustomize(ResultCode.USER_PASSWORD_ERROR);
        }
        //账号为空
        if (e instanceof UsernameNotFoundException){
            result = ResultTool.failCustomize(ResultCode.USER_LOGIN_FAILED,e.getMessage());
        }
        //找不到身份凭证异常（密码）
        if (e instanceof AuthenticationCredentialsNotFoundException){
            result = ResultTool.failCustomize(ResultCode.USER_PASSWORD_IS_BLANK);
        }
        //密码过期
        if (e instanceof CredentialsExpiredException){
            result = ResultTool.failCustomize(ResultCode.USER_PASSWORD_EXPIRED);
        }
        //账户锁定
        if (e instanceof LockedException){
            result = ResultTool.failCustomize(ResultCode.USER_ACCOUNT_LOCKED,e.getMessage());
        }
        //改变逻辑，这里由于其他的什么LockedException，DisabledException异常都会被这个异常给覆盖，索性就统一在这里返回
        //消息为异常抛出时的消息就可以分辨是什么异常了
        if (e instanceof InternalAuthenticationServiceException){
            result = ResultTool.failCustomize(ResultCode.USER_LOGIN_FAILED,e.getMessage());
        }
        //权限不足,登录应该没有这个异常可能
//        if (e instanceof InsufficientAuthenticationException){
//            result = ResultTool.failCustomize(ResultCode.USER_NO_PERMISSION,null);
//        }
        this.WriteJson(httpServletRequest,httpServletResponse,result);
    }

}
